home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Networking / OTStreamLogViewer1.0b1 / IC Libraries / ICMiscSubs.p < prev   
Encoding:
Text File  |  1998-03-15  |  24.9 KB  |  778 lines  |  [TEXT/CWIE]

  1. unit ICMiscSubs;
  2.  
  3. (*    This file is part of the Internet Configuration system and is placed in the public domain for the benefit of all.
  4.  
  5.     This file holds all those miscellaneous little functions that are basically wrappers
  6.     around existing OS functionality.
  7. *)
  8.  
  9. interface
  10.  
  11.     uses
  12.         Files,
  13.         Windows,
  14.         Lists, 
  15.         AppleEvents, 
  16.  
  17.         InternetConfig;
  18.  
  19.     (* ***** QuickDraw Stuff ***** *)
  20.  
  21.     procedure DrawIcon (resourceID: integer; const iconRect: Rect; drawHighlighted: Boolean);
  22.         (* This routine draws in icon from the resources specified by resourceID.
  23.             If the System 7 icon utilities are available, it uses the icon family
  24.             resources 'icl8', and draws using the icon utilities.  If they're not available,
  25.             it uses the 'ICN#' resource and draws using PlotIcon.
  26.         *)
  27.     
  28.     procedure MagicMarkerMode;
  29.         (* This routine sets the HiliteMode low memory global such that the
  30.             next invert operation is done using the user specified highlight colour.
  31.             If Colour QuickDraw isn't available, it does nothing.
  32.         *)
  33.  
  34.     (* ***** Event Manager Stuff ***** *)
  35.  
  36.     function DirtyKey (typedChar: char): Boolean;
  37.         (* This function returns true if the given character will cause a Text
  38.             Edit field to become dirty, ie it's a character that will go into
  39.             the field rather than move the insertion point.
  40.         *)
  41.         
  42.     function IsKeyDown (keyCode: integer): Boolean;
  43.         (* Returns true if the given virtual key is down. *)
  44.  
  45.     (* ***** Window Manager Stuff ***** *)
  46.  
  47.     (* EnterWindow, ExitWindow and the SavedWindowState type are used to implement
  48.         a standard mechanism for saving and restoring window information.  You call
  49.         EnterWindow when you want to work on a window.  This sets up the parameters
  50.         you need and saves the old parameters in the SavedWindowState variable.
  51.         You then call ExitWindow to restore that state.
  52.     *)
  53.     
  54.     type
  55.         SavedWindowInfo =
  56.             record
  57.                 oldPort: GrafPtr;
  58.                 thisPort: GrafPtr;
  59.                 font: integer;
  60.                 size: integer;
  61.                 face: Style;
  62.             end;
  63.  
  64.     procedure EnterWindow (window: WindowPtr; font, size: integer; face: Style;
  65.                                                 var saved: SavedWindowInfo);
  66.         (* Set thePort to window and establish the various window state parameters.
  67.             Save the old parameters in saved.
  68.         *)
  69.         
  70.     procedure ExitWindow (const saved: SavedWindowInfo);
  71.         (* Recover the window parameters from saved. *)
  72.  
  73.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  74.         (* Returns the window's content region. This is the region currently
  75.             being used, not a copy.  Do not munge it!
  76.         *)
  77.         
  78.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  79.         (* Returns the window's structure region. This is the region currently
  80.             being used, not a copy.  Do not munge it!
  81.         *)
  82.  
  83.     function TitleBarOnScreen (theWindow: WindowPtr): Boolean;
  84.         (* Returns true if the window's title bar is on the screen.
  85.             Note that this routine only works if the window is visible,
  86.             ie you have called ShowWindow on it.  The standard mechanism
  87.             for using this routine is to ShowWindow the window, then
  88.             call TitleBarOnScreen.  If it returns true, everything is cool.
  89.             Otherwise the window is completely off the screen, so you can
  90.             move it back on without causing visible effects.
  91.         *)
  92.         
  93.     procedure GetWindowRect (theWindow: WindowPtr; var windowRect: Rect);
  94.         (* This routine sets windowRect to the global co-ordinates of 
  95.             the position of the window.  It's typically used for saving window
  96.             state.
  97.         *)
  98.         
  99.     (* ***** Menu Manager Stuff ***** *)
  100.  
  101.     procedure SetMenuItemEnable (menuH: MenuHandle; item: integer; enable: Boolean);
  102.         (* Enable the item in the MenuHandle if enable is set, disable it otherwise.
  103.             You've gotta wonder why this isn't in the operating system!
  104.         *)
  105.         
  106.     function FindMenuItem (menuH: MenuHandle; itemTextToSearchFor: Str255; 
  107.                                                 var indexOfItemFound: integer): Boolean;
  108.         (* This routine searches through the Menu Handle looking for
  109.             itemTextToSearchFor.  If it finds it, it returns true and sets
  110.             indexOfItemFound to the position of the matching menu item.
  111.         *)
  112.  
  113.     (* ***** List Manager Stuff ***** *)
  114.  
  115.     (* All of these List Manager routines are really targetted at one dimensional
  116.         vertical lists.  They don't work well for two dimensional or horizontal
  117.         lists.
  118.     *)
  119.     
  120.     procedure InitListManagerMiscSubs;
  121.         (* The LDoKey function requires a bunch of global state to implement
  122.             it's "select by typing" function. This routine initialises that
  123.             information.
  124.         *)
  125.     
  126.     procedure LSetNoSelection (listH: ListHandle);
  127.         (* This routine clears any selection in the list. *)
  128.  
  129.     procedure LSelectAll(listH: ListHandle);
  130.         (* This routine selects the entire contents of the list. *)
  131.                 
  132.     procedure LSetSingleSelection (listH: ListHandle; row: integer);
  133.         (* This routine selects the single cell (0, row) in the list. *)
  134.  
  135.     (* The LDoKey routine takes a procedural parameter that is uses to fetch
  136.         the text associated with an item in the list so that it can implement
  137.         its "select by typing" function.
  138.     *)
  139.     type
  140.         GetListCellTextProcType = procedure(listH: ListHandle; listCell: Cell; var cellText: Str255);
  141.  
  142.     procedure LDoKey(listH: ListHandle; var event:EventRecord; getCellText: GetListCellTextProcType);
  143.         (* This routine processes a key event associated with a list, including
  144.             "select by typing".  You can disable this function by passing nil to
  145.             getCellText.
  146.         *)
  147.  
  148.     function LSelectedLine (lh: ListHandle): integer;
  149.         (* This function returns the vertical position of the first selected
  150.             cell in the list, or -1 if there is no selected cell.
  151.         *)
  152.  
  153.     function LIsEmpty (lh: ListHandle): Boolean;
  154.         (* This function returns true if the list is empty. *)
  155.         
  156.     (* ***** Truly Misc Stuff ***** *)
  157.  
  158.     function AEGotRequiredParams (const theAppleEvent: AppleEvent): OSStatus;
  159.         (* Returns no error if you've extracted all of the required
  160.             parameters out of the AppleEvent.
  161.         *)
  162.  
  163. implementation
  164.  
  165.     uses
  166.         Icons, 
  167.         Errors, 
  168.         Resources, 
  169.         Dialogs, 
  170.         ToolUtils, 
  171.         Traps, 
  172.         LowMem,
  173.         GestaltEqu,
  174.  
  175.         InternetConfig,
  176.         
  177.         ICCommonSubs;
  178.  
  179.     (* ***** QuickDraw Stuff ***** *)
  180.  
  181.     procedure DrawIcon (resourceID: integer; const iconRect: Rect; drawHighlighted: Boolean);
  182.         (* See comment in interface part. *)
  183.         var
  184.             junk: OSStatus;
  185.             iconSuite: Handle;
  186.             transform: integer;
  187.             iconH: Handle;
  188.             tmpIconRect : Rect;
  189.     begin
  190.         tmpIconRect := iconRect;
  191.         if GetIconSuite(iconSuite, resourceID, svAllLargeData) = noErr then begin
  192.             if drawHighlighted then begin
  193.                 transform := ttSelected;
  194.             end else begin
  195.                 transform := ttNone;
  196.             end; (* if *)
  197.             junk := PlotIconSuite(tmpIconRect, 0, transform, iconSuite);
  198.             junk := DisposeIconSuite(iconSuite, false);
  199.         end else begin
  200.             iconH := Get1Resource('ICN#', resourceID);
  201.             if iconH <> nil then begin
  202.                 PlotIcon(tmpIconRect, iconH);
  203.             end; (* if *)
  204.         end; (* if *)
  205.     end; (* DrawIcon *)
  206.  
  207.     procedure MagicMarkerMode;
  208.         (* See comment in interface part. *)
  209.         var
  210.             hasColourQD : Boolean;
  211.             response : longint;
  212.     begin
  213.         hasColourQD := (Gestalt(gestaltQuickdrawVersion, response) = noErr) &
  214.                         (response >= gestalt8BitQD);
  215.         if hasColourQD then begin
  216.             LMSetHiliteMode(band(LMGetHiliteMode, $7F));
  217.         end; (* if *)
  218.     end; (* MagicMarkerMode *)
  219.  
  220.     (* ***** Event Manager Stuff ***** *)
  221.  
  222.     function DirtyKey (typedChar: char): Boolean;
  223.         (* See comment in interface part. *)
  224.     begin
  225.         DirtyKey := not(typedChar in [kHomeChar, kEndChar, kHelpChar, kPageUpChar, kPageDownChar,
  226.                                 kLeftArrowChar, kRightArrowChar, kUpArrowChar, kDownArrowChar]);
  227.     end; (* DirtyKey *)
  228.  
  229.     function IsKeyDown (keyCode: integer): Boolean;
  230.         (* See comment in interface part. *)
  231.         var
  232.             currentKeys: KeyMap;
  233.     begin
  234.         GetKeys(currentKeys);
  235.         IsKeyDown := currentKeys[keyCode];
  236.     end; (* IsKeyDown *)
  237.  
  238.     (* ***** Window Manager Stuff ***** *)
  239.  
  240.     procedure EnterWindow (window: WindowPtr; font, size: integer; face: Style;
  241.                                                 var saved: SavedWindowInfo);
  242.         (* See comment in interface part. *)
  243.     begin
  244.         GetPort(saved.oldPort);
  245.         SetPort(window);
  246.         saved.thisPort := window;
  247.         saved.font := window^.txFont;
  248.         saved.size := window^.txSize;
  249.         saved.face := window^.txFace;
  250.         TextFont(font);
  251.         TextSize(size);
  252.         TextFace(face);
  253.     end; (* EnterWindow *)
  254.  
  255.     procedure ExitWindow (const saved: SavedWindowInfo);
  256.         (* See comment in interface part. *)
  257.     begin
  258.         SetPort(saved.thisPort);
  259.         TextFont(saved.font);
  260.         TextSize(saved.size);
  261.         TextFace(saved.face);
  262.         SetPort(saved.oldPort);
  263.     end; (* ExitWindow *)
  264.  
  265.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  266.         (* See comment in interface part. *)
  267.     begin
  268.         GetWindowContentRegion := WindowPeek(theWindow)^.contRgn;
  269.     end; (* GetWindowContentRegion *)
  270.  
  271.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  272.         (* See comment in interface part. *)
  273.     begin
  274.         GetWindowStructureRegion := WindowPeek(theWindow)^.strucRgn;
  275.     end; (* GetWindowStructureRegion *)
  276.  
  277.     function TitleBarOnScreen (theWindow: WindowPtr): Boolean;
  278.         (* See comment in interface part. *)
  279.         var
  280.             result : Boolean;
  281.             titleBarRegion: RgnHandle;
  282.     begin
  283.         result := true;
  284.         titleBarRegion := NewRgn;
  285.         if titleBarRegion <> nil then begin
  286.             (* First calculate the title bar region by subtracting the content
  287.                 region away from the structure region.
  288.             *)
  289.             CopyRgn(GetWindowStructureRegion(theWindow), titleBarRegion);
  290.             DiffRgn(titleBarRegion, GetWindowContentRegion(theWindow), titleBarRegion);
  291.             
  292.             (* Now intersect the title bar region with the grey region, ie the region
  293.                 describing the extent of the desktop and return true if the intersection
  294.                 is not empty.
  295.             *)
  296.             SectRgn(titleBarRegion, GetGrayRgn, titleBarRegion);
  297.             result := not EmptyRgn(titleBarRegion);
  298.             DisposeRgn(titleBarRegion);
  299.         end; (* if *)
  300.         TitleBarOnScreen := result;
  301.     end; (* TitleBarOnScreen *)
  302.  
  303.     procedure GetWindowRect (theWindow: WindowPtr; var windowRect: Rect);
  304.         (* See comment in interface part. *)
  305.         var
  306.             oldPort : GrafPtr;
  307.     begin
  308.         GetPort(oldPort);
  309.         SetPort(theWindow);
  310.         windowRect := WindowPeek(theWindow)^.port.portRect;
  311.         LocalToGlobal(windowRect.topLeft);
  312.         LocalToGlobal(windowRect.botRight);
  313.         SetPort(oldPort);
  314.     end; (* GetWindowRect *)
  315.  
  316.     (* ***** Menu Manager Stuff ***** *)
  317.  
  318.     procedure SetMenuItemEnable (menuH: MenuHandle; item: integer; enable: Boolean);
  319.         (* See comment in interface part. *)
  320.     begin
  321.         if enable then begin
  322.             EnableItem(menuH, item);
  323.         end else begin
  324.             DisableItem(menuH, item);
  325.         end; (* if *)
  326.     end; (* SetMenuItemEnable *)
  327.  
  328.     function FindMenuItem (menuH: MenuHandle; itemTextToSearchFor: Str255; 
  329.                                                 var indexOfItemFound: integer): Boolean;
  330.         (* See comment in interface part. *)
  331.         var
  332.             itemIndex: integer;
  333.             itemText: Str255;
  334.     begin
  335.         FindMenuItem := false;
  336.         for itemIndex := 1 to CountMItems(menuH) do begin
  337.             GetMenuItemText(menuH, itemIndex, itemText);
  338.             if IUEqualString(itemText, itemTextToSearchFor) = 0 then begin
  339.                 indexOfItemFound := itemIndex;
  340.                 FindMenuItem := true;
  341.             end; (* if *)
  342.         end; (* for *)
  343.     end; (* FindMenuItem *)
  344.  
  345.     (* ***** List Manager Stuff ***** *)
  346.  
  347.     var
  348.         gCharsTypedSoFar: Str255;
  349.         gTimeOfLastCharTyped: longint;
  350.         gListHandleOfLastCharTyped: ListHandle;
  351.  
  352.     procedure InitListManagerMiscSubs;
  353.         (* See comment in interface part. *)
  354.     begin
  355.         gCharsTypedSoFar := '';
  356.         gTimeOfLastCharTyped := 0;
  357.         gListHandleOfLastCharTyped := nil;
  358.     end; (* InitListManagerMiscSubs *)
  359.  
  360.     procedure LSetNoSelection (listH: ListHandle);
  361.         (* See comment in interface part. *)
  362.         var
  363.             listCell: Cell;
  364.     begin
  365.         listCell.v := 0;
  366.         listCell.h := 0;
  367.         while LGetSelect(true, listCell, listH) do begin
  368.             LSetSelect(false, listCell, listH);
  369.             listCell.v := listCell.v + 1;
  370.             listCell.h := 0;
  371.         end; (* if *)
  372.     end; (* LSetNoSelection *)
  373.     
  374.     procedure LSelectAll(listH: ListHandle);
  375.         var
  376.             listCell: Cell;
  377.             row: integer;
  378.     begin
  379.         for row := 0 to listH^^.dataBounds.bottom - 1 do begin
  380.             listCell.v := row;
  381.             listCell.h := 0;
  382.             LSetSelect(true, listCell, listH);
  383.         end; (* for *)
  384.     end; (* LSelectAll *)
  385.  
  386.     procedure LSetSingleSelection (listH: ListHandle; row: integer);
  387.         (* See comment in interface part. *)
  388.         var
  389.             listCell: Cell;
  390.     begin
  391.         listCell.h := 0;
  392.         listCell.v := row;
  393.         LSetSelect(true, listCell, listH);
  394.         listCell.v := 0;
  395.         listCell.h := 0;
  396.         while LGetSelect(true, listCell, listH) do begin
  397.             if listCell.v <> row then begin
  398.                 LSetSelect(false, listCell, listH);
  399.             end; (* if *)
  400.             listCell.v := listCell.v + 1;
  401.             listCell.h := 0;
  402.         end; (* while *)
  403.         LAutoScroll(listH);
  404.     end; (* LSetSingleSelection *)
  405.  
  406.     function LGetUniqueEntryName (listH: ListHandle; listCell: Cell; getCellText: GetListCellTextProcType): Str255;
  407.         (* This function calls getCellText and then returns a 'uniquified' version of the
  408.             cell text.  What that means is that it returns the cell text followed by
  409.             a chr(0) followed by the the vertical co-ordinate of the cell encoded
  410.             as two characters.  This is useful because it allows functions that
  411.             need to distinguish between two cells even if they have the same
  412.             name to function, eg tabbing.
  413.         *)
  414.         var
  415.             result: Str255;
  416.     begin
  417.         result := '';
  418.         getCellText(listH, listCell, result);
  419.         LGetUniqueEntryName := concat(result, chr(0), 
  420.                                                                         chr(listCell.v div 256), 
  421.                                                                         chr(listCell.v mod 256));
  422.     end; (* LGetUniqueEntryName *)
  423.  
  424.     function LGetSelectedCellCommon (listH: ListHandle; var listCell: Cell; getCellText: GetListCellTextProcType;
  425.                                                             first : Boolean): Boolean;
  426.         (* This function finds the alphabetically first or last cell (depending
  427.             on the value of first) in the currently selected cells of the list.  It
  428.             returns false if there are no selected cells.
  429.         *)
  430.         var
  431.             result : Boolean;
  432.             cellText: Str255;
  433.             bestText : Str255;
  434.             indexOfBestText: integer;
  435.     begin
  436.         (* Establish some pre-conditions. *)
  437.         result := false;
  438.         listCell.h := 0;
  439.         listCell.v := 0;
  440.         indexOfBestText := 0;
  441.         if first then begin
  442.             bestText := concat(chr(255), chr(255));
  443.         end else begin
  444.             bestText := '';
  445.         end; (* if *)
  446.         
  447.         (* Loop through the selected cells, looking for the best text (ie the
  448.             alphabetically first or last).
  449.         *)
  450.         while LGetSelect(true, listCell, listH) do begin
  451.             result := true;
  452.             getCellText(listH, listCell, cellText);
  453.             if (first & (IUCompString(cellText, bestText) < 0)) |
  454.                         (not first & (IUCompString(cellText, bestText) > 0)) then begin
  455.                 indexOfBestText := listCell.v;
  456.                 bestText := cellText;
  457.             end; (* if *)
  458.             listCell.v := listCell.v + 1;
  459.         end; (* while *)
  460.         
  461.         (* Finish up. *)
  462.         listCell.h := 0;
  463.         listCell.v := indexOfBestText;
  464.         LGetSelectedCellCommon := result;
  465.     end; (* LGetSelectedCellCommon *)
  466.  
  467.     function LGetFirstSelectedCell (listH: ListHandle; var listCell: Cell; getCellText: GetListCellTextProcType): Boolean;
  468.         (* This function finds the alphabetically first cell in the currently
  469.             selected cells of the list.  It returns false if there are no selected cells.
  470.         *)
  471.     begin
  472.         LGetFirstSelectedCell := LGetSelectedCellCommon(listH, listCell, getCellText, true);
  473.     end; (* LGetFirstSelectedCell *)
  474.  
  475.     function LGetLastSelectedCell (listH: ListHandle; var listCell: Cell; getCellText: GetListCellTextProcType): Boolean;
  476.         (* This function finds the alphabetically last cell in the currently
  477.             selected cells of the list.  It returns false if there are no selected cells.
  478.         *)
  479.     begin
  480.         LGetLastSelectedCell := LGetSelectedCellCommon(listH, listCell, getCellText, false);
  481.     end; (* LGetLastSelectedCell *)
  482.  
  483.     function LSelectFirstCommon (listH: ListHandle; markerText: Str255; getCellText: GetListCellTextProcType;
  484.                                                     before : Boolean; orEqual : Boolean): Boolean;
  485.         (* This function selects the first cell alphabetically before (or after, depending
  486.             on the value of "before") the markerText.  If returns true if it managed to do this,
  487.             false otherwise.  The orEqual value determines whether an
  488.             equal value is considered to be before the otherwise best value.
  489.         *)
  490.         var
  491.             result: Boolean;
  492.             row : integer;
  493.             indexOfBestText: integer;
  494.             listCell: Cell;
  495.             bestText : Str255;
  496.             cellText: Str255;
  497.             comp1 : integer;
  498.             comp2 : integer;
  499.     begin
  500.         (* Establish some pre-conditions. *)
  501.         result := false;
  502.         indexOfBestText := 0;
  503.         if before then begin
  504.             bestText := '';
  505.         end else begin
  506.             bestText := concat(chr(255), chr(255));
  507.         end; (* if *)
  508.         
  509.         (* Iterate through all the cells, looking for best text.  Best is defined
  510.             as the phone that's alphabetically before (or after, depending on
  511.             the value of before) the markerText.
  512.         *)
  513.         for row := 0 to listH^^.dataBounds.bottom - 1 do begin
  514.             listCell.h := 0;
  515.             listCell.v := row;
  516.             getCellText(listH, listCell, cellText);
  517.  
  518.             (* OK, so this needs some explaning (-:
  519.                 comp1 and comp2 just cache the value of the comparisons between
  520.                 markerText, cellText and bestText.
  521.                 
  522.                 If before is true, we're looking for the cell immediately before
  523.                 the markerText.  This means that the markerText must be
  524.                 greater than (ie "comp1 > 0") or equal to (ie "| (comp1 = 0)")
  525.                 the cellText, and the cellText must be greater than (ie "comp2 > 0")
  526.                 the bestText we've found so far.
  527.                 
  528.                 If before is false, we're looking for the cell immediately after
  529.                 the markerText.  This means that the markerText must be
  530.                 less than (ie "comp1 < 0") or equal to (ie "| (comp1 = 0)")
  531.                 the cellText, and the cellText must be less than (ie "comp2 < 0")
  532.                 the bestText we've found so far.
  533.                 
  534.                 *phew*
  535.             *)
  536.             comp1 := IUCompString(markerText, cellText);
  537.             comp2 := IUCompString(cellText, bestText);
  538.             if (        before & (((comp1 > 0) | ((comp1 = 0) & orEqual)) & (comp2 > 0))) |
  539.                 (not before & (((comp1 < 0) | ((comp1 = 0) & orEqual)) & (comp2 < 0))) then begin
  540.                 bestText := cellText;
  541.                 indexOfBestText := listCell.v;
  542.                 result := true;
  543.             end; (* if *)
  544.         end; (* for *)
  545.         
  546.         (* Now set the selection to the cell we found. *)
  547.         if result then begin
  548.             LSetSingleSelection(listH, indexOfBestText);
  549.         end; (* if *)
  550.         LSelectFirstCommon := result;
  551.     end; (* LSelectFirstCommon *)
  552.  
  553.     function LSelectFirstBefore (listH: ListHandle; beforeThis: Str255; getCellText: GetListCellTextProcType): Boolean;
  554.         (* This function selects the first cell alphabetically before
  555.             the beforeThis text.  If returns true if it managed to do this,
  556.             false otherwise.  The orEqual value determines whether an
  557.             equal value is considered to be before the otherwise best value.
  558.         *)
  559.     begin
  560.         LSelectFirstBefore := LSelectFirstCommon(listH, beforeThis, getCellText, true, false);
  561.     end; (* LSelectFirstBefore *)
  562.     
  563.     function LSelectFirstAfter (listH: ListHandle; afterThis: Str255; getCellText: GetListCellTextProcType; orEqual:Boolean): Boolean;
  564.         (* This function selects the first cell alphabetically after
  565.             the afterThis text.  If returns true if it managed to do this,
  566.             false otherwise.  The orEqual value determines whether an
  567.             equal value is considered to be before the otherwise best value.
  568.         *)
  569.     begin
  570.         LSelectFirstAfter := LSelectFirstCommon(listH, afterThis, getCellText, false, orEqual);
  571.     end; (* LSelectFirstAfter *)
  572.  
  573.     procedure LDownArrow(listH : ListHandle);
  574.         (* Find the last selected cell and select the cell after it. *)
  575.         var
  576.             listCell : Cell;
  577.             indexOfCellToSelect : integer;
  578.     begin
  579.         listCell.h := 0;
  580.         listCell.v := 0;
  581.         indexOfCellToSelect := 0;
  582.         while LGetSelect(true, listCell, listH) do begin
  583.             listCell.v := listCell.v + 1;
  584.             indexOfCellToSelect := listCell.v;
  585.         end; (* if *)
  586.         if indexOfCellToSelect >= listH^^.dataBounds.bottom then begin
  587.             indexOfCellToSelect := listH^^.dataBounds.bottom - 1;
  588.         end; (* if *)
  589.         LSetSingleSelection(listH, indexOfCellToSelect);
  590.         LAutoScroll(listH);
  591.     end; (* LDownArrow *)
  592.  
  593.     procedure LUpArrow(listH : ListHandle);
  594.         (* Find the first selected cell and select the cell before it. *)
  595.         var
  596.             listCell : Cell;
  597.     begin
  598.         listCell.h := 0;
  599.         listCell.v := 0;
  600.         if not LGetSelect(true, listCell, listH) then begin
  601.             listCell.v := listH^^.dataBounds.bottom;
  602.         end; (* if *)
  603.         if listCell.v > 0 then begin
  604.             listCell.v := listCell.v - 1;
  605.         end; (* if *)
  606.         LSetSingleSelection(listH, listCell.v);
  607.         LAutoScroll(listH);
  608.     end; (* LUpArrow *)
  609.     
  610.     procedure LTab(listH : ListHandle; getCellText: GetListCellTextProcType; shift : Boolean);
  611.         (* Handle Tab and shift-Tab keys in the list. *)
  612.         var
  613.             junkBool : Boolean;
  614.             listCell : Cell;
  615.             done : Boolean;
  616.             selectedCellText : Str255;
  617.     begin
  618.         if getCellText <> nil then begin
  619.             if not shift then begin
  620.                 (* Tab -- If there are selected cells then attempt to select the first
  621.                     cell after the last selected cell.  If we can't or there were no 
  622.                     selected cells, then select the first cell alphabetically.
  623.                 *)
  624.                 done := false;
  625.                 if LGetLastSelectedCell(listH, listCell, getCellText) then begin
  626.                     selectedCellText := LGetUniqueEntryName(listH, listCell, getCellText);
  627.                     if LSelectFirstAfter(listH, selectedCellText, getCellText, false) then begin
  628.                         done := true;
  629.                     end; (* if *)
  630.                 end; (* if *)
  631.                 if not done then begin
  632.                     junkBool := LSelectFirstAfter(listH, '', getCellText, false);
  633.                 end; (* if *)
  634.             end else begin
  635.                 (* shift-Tab -- If there are no selected cells then attempt to select the
  636.                     cell before the first selected cell.  If we can't or there were no 
  637.                     selected cells, then select the last cell alphabetically.
  638.                 *)
  639.                 done := false;
  640.                 if LGetFirstSelectedCell(listH, listCell, getCellText) then begin
  641.                     getCellText(listH, listCell, selectedCellText);
  642.                     if LSelectFirstBefore(listH, selectedCellText, getCellText) then begin
  643.                         done := true;
  644.                     end; (* if *)
  645.                 end; (* if *)
  646.                 if not done then begin
  647.                     junkBool := LSelectFirstBefore(listH, chr(255), getCellText);
  648.                 end; (* if *)
  649.             end; (* if *)
  650.         end; (* if *)
  651.     end; (* LTab *)
  652.  
  653.     procedure LOtherKey(listH : ListHandle; getCellText : GetListCellTextProcType;
  654.                                             typedChar : char; eventTicks : longint);
  655.         (* This routine handles the pressing of a normaly key in a list
  656.             by selecting the cell best associated with the text typed so far.
  657.         *)
  658.         var
  659.             junkBool : Boolean;
  660.     begin
  661.         if (getCellText <> nil) & (typedChar >= ' ') then begin
  662.             if eventTicks - gTimeOfLastCharTyped > 60 then begin
  663.                 gCharsTypedSoFar := '';
  664.             end; (* if *)
  665.             gTimeOfLastCharTyped := eventTicks;
  666.             gCharsTypedSoFar := concat(gCharsTypedSoFar, typedChar);
  667.             if not LSelectFirstAfter(listH, gCharsTypedSoFar, getCellText, true) then begin
  668.                 junkBool := LSelectFirstBefore(listH, chr(255), getCellText);
  669.             end; (* if *)
  670.         end; (* if *)
  671.     end; (* LOtherKey *)
  672.  
  673.     procedure LDoKey(listH: ListHandle; var event:EventRecord; getCellText: GetListCellTextProcType);
  674.         (* See comment in interface part. *)
  675.         var
  676.             eventTicks: longint;
  677.             typedChar:char;
  678.     begin
  679.         eventTicks := event.when;
  680.         typedChar := chr(band(event.message, charCodeMask));
  681.  
  682.         (* First up, if we've changed lists or typed a control character,
  683.             we reset the globals that track the current typing state.
  684.         *)
  685.         if (gListHandleOfLastCharTyped <> listH) or (typedChar < ' ') then begin
  686.             gTimeOfLastCharTyped := 0;
  687.             gListHandleOfLastCharTyped := listH;
  688.         end; (* if *)
  689.         
  690.         (* Now dispatch the various characters type. *)
  691.         case typedChar of
  692.             (* Handle the trivial scrolling around keys. *)
  693.             kHomeChar:
  694.                 LScroll(0, -listH^^.dataBounds.bottom, listH);
  695.             kEndChar:
  696.                 LScroll(0, listH^^.dataBounds.bottom, listH);
  697.             kPageUpChar:
  698.                 LScroll(0, -(listH^^.visible.bottom - listH^^.visible.top - 2), listH);
  699.             kPageDownChar:
  700.                 LScroll(0, (listH^^.visible.bottom - listH^^.visible.top - 2), listH);
  701.         
  702.             (* Handle up and down arrows. *)
  703.             kDownArrowChar:
  704.                 LDownArrow(listH);
  705.             kUpArrowChar:
  706.                 LUpArrow(listH);
  707.                 
  708.             (* Tab and shift-Tab and other keys are trickier. *)
  709.             kTabChar:
  710.                 LTab(listH, getCellText, band(event.modifiers, shiftKey) <> 0);
  711.             otherwise
  712.                 LOtherKey(listH, getCellText, typedChar, eventTicks);
  713.         end; (* case *)
  714.     end; (* LDoKey *)
  715.  
  716.     function LSelectedLine (lh: ListHandle): integer;
  717.         (* See comment in interface part. *)
  718.         var
  719.             listCell: Cell;
  720.     begin
  721.         SetPt(listCell, 0, 0);
  722.         if LGetSelect(true, listCell, lh) then begin
  723.             LSelectedLine := listCell.v;
  724.         end else begin
  725.             LSelectedLine := -1;
  726.         end; (* if *)
  727.     end; (* LSelectedLine *)
  728.  
  729.     function LIsEmpty (lh: ListHandle): Boolean;
  730.         (* See comment in interface part. *)
  731.     begin
  732.         LIsEmpty := lh^^.dataBounds.bottom <= lh^^.dataBounds.top;
  733.     end; (* LIsEmpty *)
  734.  
  735.     (* ***** Truly Misc Stuff ***** *)
  736.  
  737.     function AEGotRequiredParams (const theAppleEvent: AppleEvent): OSStatus;
  738.         (* See comment in interface part. *)
  739.         var
  740.             typeCode: DescType;
  741.             actualSize: Size;
  742.             err: OSStatus;
  743.             tmpAppleEvent : AppleEvent;
  744.     begin
  745.         tmpAppleEvent := theAppleEvent;
  746.         err := AEGetAttributePtr(tmpAppleEvent, keyMissedKeywordAttr, typeWildCard, typeCode, nil, 0, actualSize);
  747.         if err = errAEDescNotFound then begin
  748.             err := noErr;
  749.         end else if err = noErr then begin
  750.             err := errAEEventNotHandled;
  751.         end; (* if *)
  752.         AEGotRequiredParams := err;
  753.     end; (* AEGotRequiredParams *)
  754.  
  755.     function NumToolboxTraps: integer;
  756.         (* Returns the number of toolbox traps on this machine. *)
  757.     begin
  758.         if NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) then begin
  759.             NumToolboxTraps := $200
  760.         end else begin
  761.             NumToolboxTraps := $400;
  762.         end; (* if *)
  763.     end; (* NumToolboxTraps *)
  764.  
  765.     function GetTrapType (theTrap: integer): TrapType;
  766.         (* Returns the trap type associated with the given A-Trap number. *)
  767.         const
  768.             TrapMask = $0800;
  769.     begin
  770.         if band(theTrap, TrapMask) > 0 then begin
  771.             GetTrapType := ToolTrap
  772.         end else begin
  773.             GetTrapType := OSTrap;
  774.         end; (* if *)
  775.     end; (* GetTrapType *)
  776.  
  777. end. (* ICMiscSubs *)
  778.